using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;

namespace AGame.TabOrm.TabOrmAttributeExpand
{
    public class ToInstance : TabField
    {
        public string[] FieldName;

        public ToInstance(params string[] fieldName)
        {
            FieldName = fieldName;
        }

        public override Func<string, object> GetStrToObjFunc(Type tabType, Type targetType)
        {
            if (targetType.IsArray)
            {
                var elementType = targetType.GetElementType();
                if (elementType == null)
                {
                    return null;
                }

                return s =>
                {
                    if (string.IsNullOrEmpty(s))
                    {
                        return Array.CreateInstance(elementType, 0);
                    }

                    var split = s.Split('|');
                    var instance = Array.CreateInstance(elementType, split.Length);
                    for (var i = 0; i < split.Length; i++)
                    {
                        instance.SetValue(CreateInstance(elementType, split[i]), i);
                    }

                    return instance;
                };
            }

            if (!targetType.IsGenericType)
            {
                return s => CreateInstance(targetType, s);
            }

            var genericTypeDefinition = targetType.GetGenericTypeDefinition();
            if (genericTypeDefinition != typeof(List<>) && genericTypeDefinition != typeof(HashSet<>))
            {
                return null;
            }

            var type = targetType.GetGenericArguments()[0];
            var methodInfo = targetType.GetMethod("Add");
            if (methodInfo == null)
            {
                return null;
            }

            return s =>
            {
                var instance = Activator.CreateInstance(targetType);
                if (string.IsNullOrEmpty(s))
                {
                    return instance;
                }

                var split = s.Split('|');
                foreach (var str in split)
                {
                    methodInfo.Invoke(instance, new[] {CreateInstance(type, str)});
                }

                return instance;
            };
        }

        public override Func<object, string> GetObjToStrFunc(Type tabType, Type targetType)
        {
            if (targetType.IsArray)
            {
                return GetInstanceStringByIEnumerable;
            }
            if (!targetType.IsGenericType)
            {
                return GetInstanceString;
            }
            var genericTypeDefinition = targetType.GetGenericTypeDefinition();
            if (genericTypeDefinition != typeof(List<>) && genericTypeDefinition != typeof(HashSet<>))
            {
                return null;
            }
            return GetInstanceStringByIEnumerable;
        }

        private string GetInstanceStringByIEnumerable(object obj)
        {
            if (obj == null || !(obj is IEnumerable iEnumerable))
            {
                return string.Empty;
            }

            var strList = (from object o in iEnumerable select GetInstanceString(o)).ToList();
            return string.Join("|", strList);
        }
        
        private object CreateInstance(Type targetType, string data)
        {
            var instance = Activator.CreateInstance(targetType);
            if (string.IsNullOrEmpty(data))
            {
                return instance;
            }
            var dataList = data.Split('+');
            var count = Math.Min(FieldName.Length, dataList.Length);
            for (var i = 0; i < count; i++)
            {
                var name = FieldName[i];
                if (string.IsNullOrEmpty(name))
                {
                    continue;
                }

                var fieldInfo = targetType.GetField(name);
                if (fieldInfo != null)
                {
                    var val = Convert.ChangeType(dataList[i], fieldInfo.FieldType);
                    fieldInfo.SetValue(instance, val);
                    continue;
                }

                var propertyInfo = targetType.GetProperty(name);
                if (propertyInfo == null)
                {
                    continue;
                }
                var val1 = Convert.ChangeType(dataList[i], propertyInfo.PropertyType);
                propertyInfo.SetValue(instance, val1);
            }
            return instance;
        }

        

        private string GetInstanceString(object instance)
        {
            var strList = new List<string>();
            var type = instance.GetType();
            foreach (var s in FieldName)
            {
                if (string.IsNullOrEmpty(s))
                {
                    strList.Add(string.Empty);
                    continue;
                }

                var fieldInfo = type.GetField(s);
                if (fieldInfo != null)
                {
                    var value = fieldInfo.GetValue(instance)?.ToString() ?? string.Empty;
                    strList.Add(value);
                    continue;
                }

                var propertyInfo = type.GetProperty(s);
                if (propertyInfo != null)
                {
                    var value = propertyInfo.GetValue(instance)?.ToString() ?? string.Empty;
                    strList.Add(value);
                    continue;
                }
                strList.Add(string.Empty);
            }
            return string.Join("+", strList);
        }
    }
}